package org.luxor.commons.jdbc.config;

import org.luxor.commons.jdbc.aspect.DynamicDataSourceAspect;
import org.luxor.commons.jdbc.aspect.EncryptHandleAspect;
import org.luxor.commons.jdbc.plugin.dynamic.datasource.DynamicDataSourceCreator;
import org.luxor.commons.jdbc.plugin.dynamic.datasource.DynamicRoutingDataSource;
import org.luxor.commons.jdbc.plugin.dynamic.interceptor.MasterSlaveAutoRoutingPlugin;
import org.luxor.commons.jdbc.plugin.dynamic.processor.*;
import org.luxor.commons.jdbc.plugin.dynamic.properties.DataSourceProperty;
import org.luxor.commons.jdbc.plugin.dynamic.properties.DynamicDataSourceProperties;
import org.luxor.commons.jdbc.plugin.dynamic.provider.DynamicDataSourceProvider;
import org.luxor.commons.jdbc.plugin.dynamic.provider.YmlDynamicDataSourceProvider;
import org.luxor.commons.jdbc.plugin.dynamic.strategy.DynamicDataSourceStrategy;
import org.luxor.commons.jdbc.plugin.encrypt.EncryptFieldInterceptor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.web.context.support.WebApplicationObjectSupport;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * 动态数据源配置
 *
 * @author Mr.Yan  @date 2019/9/25$
 */
@Configuration
@EnableConfigurationProperties({DynamicDataSourceProperties.class, DataSourceProperty.class})
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@ConditionalOnProperty(prefix = DynamicDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class DynamicDataSourceConfig extends WebApplicationObjectSupport {

    @Resource
    private DynamicDataSourceProperties properties;

    /**
     * 纯读写分离
     */
    @Bean
    public MasterSlaveAutoRoutingPlugin masterSlaveAutoRoutingPlugin() {
        return new MasterSlaveAutoRoutingPlugin();
    }

    /**
     * 多数据源切换 AOP
     */
    @Bean
    public DynamicDataSourceAspect dynamicDataSourceAspect(DsProcessor dsProcessor) {
        return new DynamicDataSourceAspect(dsProcessor);
    }

    @Bean
    public DynamicDataSourceCreator dynamicDataSourceCreator() {
        DynamicDataSourceCreator dynamicDataSourceCreator = new DynamicDataSourceCreator();
        dynamicDataSourceCreator.setHikariGlobalConfig(properties.getHikari());
        dynamicDataSourceCreator.setGlobalPublicKey(properties.getPublicKey());
        return dynamicDataSourceCreator;
    }

    /**
     * 加载所有数据源配置
     */
    @Bean
    public DynamicDataSourceProvider dynamicDataSourceProvider() {
        return new YmlDynamicDataSourceProvider(properties);
    }

    /**
     * 动态数据源路由
     */
    @Bean
    @Primary
    public DataSource dataSource(DynamicDataSourceProvider provider) {
        Class<? extends DynamicDataSourceStrategy> strategy = properties.getStrategy();
        String primary = properties.getPrimary();
        boolean p6spy = properties.getP6spy();
        boolean strict = properties.getStrict();
        boolean seata = properties.getSeata();
        return new DynamicRoutingDataSource(provider, strategy, strict, primary, p6spy, seata);
    }

    /**
     * 决定要选择的数据源
     */
    @Bean
    public DsProcessor dsProcessor(ObjectProvider<Environment> environment) {
        DsHeaderProcessor headerProcessor = new DsHeaderProcessor();
        DsSessionProcessor sessionProcessor = new DsSessionProcessor();
        DsValueProcessor valueProcessor = new DsValueProcessor(environment.getIfAvailable());
        DsSpelExpressionProcessor spelExpressionProcessor = new DsSpelExpressionProcessor();
        headerProcessor.setNextProcessor(sessionProcessor);
        sessionProcessor.setNextProcessor(valueProcessor);
        valueProcessor.setNextProcessor(spelExpressionProcessor);
        return headerProcessor;
    }


    /**
     * 方法参数脱敏处理AOP
     */
    @Bean
    @ConditionalOnMissingBean
    public EncryptHandleAspect encryptHandleAspect(DynamicDataSourceProperties properties) {
        return new EncryptHandleAspect(properties);
    }

    /**
     * 对象属性脱敏处理拦截
     */
    @Bean
    @ConditionalOnMissingBean
    public EncryptFieldInterceptor encryptFieldInterceptor(DynamicDataSourceProperties properties) {
        return new EncryptFieldInterceptor(properties);
    }
}
